﻿#region Copyright © 2010 Chad Albrecht & Mark Adams
/*
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/

/* © BLOKUS and associated trademarks are copyright Mattel, Inc.   */
#endregion

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Collections.Generic;

namespace Blokus
{
    public class Square
    {
        private GameBoard parent;
        private Rectangle squareRect;
        private int row, column;
        private bool highlight;

        public const int SQUARE_ZINDEX = 0;
       
        public Square(GameBoard parent, int row, int column)
        {
            this.squareRect = new Rectangle();            
            this.squareRect.Fill = new SolidColorBrush(Color.FromArgb(255, 128, 121, 128));
            this.UpdateHighlight();
            Grid.SetRow(this.squareRect, row);
            Grid.SetColumn(this.squareRect, column);           

            this.parent = parent;
            this.parent.LayoutRoot.Children.Add(this.squareRect);

            this.row = row;
            this.column = column;

            this.Status = SquareStatus.None;

            Canvas.SetZIndex(this.squareRect, SQUARE_ZINDEX);
            
        }

        public enum SquareStatus
        {
            Player1,
            Player2,
            MAX_PLAYERS,
            None,
            Usable,           
            Invalid
        }

        private SquareStatus _status;
        public SquareStatus Status
        {
            get { return _status; }
            set { _status = value; }
        }


        public bool Highlight
        {
            get { return this.highlight; }
            set
            {
                this.highlight = value;
                this.UpdateHighlight();
            }
        }
       
        internal void ShowStarterMark(Color color)
        {
            string name = color.ToString() + "_StarterMark";

            Ellipse circle = new Ellipse();

            circle.Width = 10;
            circle.Height = 10;

            Grid.SetRow(circle, row);
            Grid.SetColumn(circle, column);
           
            circle.Fill = new SolidColorBrush(color);
            circle.Name = name;

            this.parent.LayoutRoot.Children.Add(circle);
        }

        private void UpdateHighlight()
        {
            if (this.highlight)
            {
                this.squareRect.StrokeThickness = 10;
                this.squareRect.Stroke = new SolidColorBrush(Colors.Blue);
            }
            else
            {
                this.squareRect.StrokeThickness = 0.5;
                this.squareRect.Stroke = new SolidColorBrush(Colors.White);
            }
        }              
    }

	public partial class GameBoard : UserControl
	{
        public const int ROWS = 14;
        public const int COLS = 14;

        private const int PURPLE_STARTER_ROW = 4;
        private const int PURPLE_STARTER_COL = 4;
        private const int ORANGE_STARTER_ROW = 9;
        private const int ORANGE_STARTER_COL = 9;
		
        private Square[][] squares = new Square[ROWS][];
		private List<Piece>       pieces = new List<Piece>();

        public delegate void GameResetHandler(object sender);
        public event GameResetHandler GameReset;
		
		public GameBoard()
		{
			// Required to initialize variables
			this.InitializeComponent();

			// Set up the board squares
            for (int row = 0; row < ROWS; row++)
			{
                RowDefinition rowDefinition = new RowDefinition();
                rowDefinition.Height = new GridLength(1.0 / 8.0, GridUnitType.Star);
                this.LayoutRoot.RowDefinitions.Add(rowDefinition);

                ColumnDefinition columnDefinition = new ColumnDefinition();
                columnDefinition.Width = new GridLength(1.0 / 8.0, GridUnitType.Star);
                this.LayoutRoot.ColumnDefinitions.Add(columnDefinition);

				this.squares[row] = new Square[COLS];
				for (int col = 0; col < COLS; col++)
				{
					this.squares[row][col] = new Square(this, row, col);

                    if ((row == PURPLE_STARTER_ROW) && (col == PURPLE_STARTER_COL))
                        this.squares[row][col].ShowStarterMark(Colors.Purple);
                    else if ((row == ORANGE_STARTER_ROW) && (col == ORANGE_STARTER_COL))
                        this.squares[row][col].ShowStarterMark(Colors.Orange);                                          
				}
			}

           
		}

        public void ClearSquareHighlights()
        {
            foreach (Square[] row in squares)
            {
                foreach (Square sq in row)
                {
                    sq.Highlight = false;
                }
            }
        }

        public Square.SquareStatus GetSquareStatus(int row, int col)
        {
            if ((col < 0) || (col > COLS - 1))
                return Square.SquareStatus.Invalid;
            else if ((row < 0) || (row > ROWS - 1))
                return Square.SquareStatus.Invalid;
            else
                return this.squares[row][col].Status;
        }

        /// <summary>
        /// Can the piece be placed on the board?
        /// </summary>
        /// <param name="piece">Piece to check</param>
        /// <returns>bool</returns>
        public bool PieceCanBePlaced(Piece piece)
        {
            bool CanBePlaced = false;

            for (int row = 0; row < ROWS && !CanBePlaced; row++)
            {
                for (int col = 0; col < COLS && !CanBePlaced; col++)
                {
                    Square sq = this.squares[row][col];
                    if (sq.Status == Square.SquareStatus.None)
                    {
                        for (int nFlip = 0; nFlip < piece.NFlips && !CanBePlaced; nFlip++)
                        {
                            for (int nRotation = 0; (nRotation < piece.NRotations) && !CanBePlaced; nRotation++)
                            {
                                piece.RotatePieceCW();
                                if (piece.Parent.IsFirstMove)
                                {
                                    CanBePlaced = PieceFits(piece, row, col) &&
                                        PieceOnStarterSquare(piece, row, col);
                                }
                                else
                                {

                                    CanBePlaced = PieceTouchesCorner(piece, row, col) &&
                                       PieceFits(piece, row, col) &&
                                       PieceDoesNotConflict(piece, row, col);
                                }
                                
                            }
                            piece.FlipPieceX();
                        }
                    }                   
                }
            }            
            return CanBePlaced;
        }
        
        /// <summary>
        /// Can the piece be placed on the board at a certain mouse location?
        /// </summary>
        /// <param name="piece">Piece to check</param>
        /// <param name="x">Mouse X location</param>
        /// <param name="y">Mouse Y location</param>
        /// <returns>bool</returns>
        public bool PieceCanBePlaced(Piece piece, double x, double y)
        {
            bool CanBePlaced = false;
            int row = -1;
            int col = -1;             
           
            // Hit Test the gameboard to find the center square
            List<UIElement> hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(new Point(x, y), this) as List<UIElement>;

            if (hits.Count > 0)
            {
#if DEBUG
                ClearSquareHighlights();
#endif
                foreach (UIElement hit in hits)
                {
                    if (hit is Rectangle)
                    {
                        Rectangle rect = (Rectangle)hit;
                        row = Grid.GetRow(rect);
                        col = Grid.GetColumn(rect);
#if DEBUG
                        List<Offset> vectors = piece.CurrentVectors;
                        foreach (Offset v in vectors)
                        {
                            int nRow = row + v.Y;
                            int nCol = col + v.X;
                            if(GetSquareStatus(nRow,nCol) == Square.SquareStatus.None)
                                this.squares[nRow][nCol].Highlight = true;

                        }
#endif
                        break;
                    }
                }
                if ((row != -1) && (col != -1))
                {
                    if (piece.Parent.IsFirstMove)
                    {
                        CanBePlaced = PieceFits(piece, row, col) &&
                            PieceOnStarterSquare(piece, row, col);
                    }
                    else
                    {

                        CanBePlaced = PieceTouchesCorner(piece, row, col) &&
                           PieceFits(piece, row, col) &&
                           PieceDoesNotConflict(piece, row, col);
                    }
                }
            }

            return CanBePlaced;
        }

        public bool PlacePiece(Piece piece, System.Windows.Input.MouseEventArgs e)
        {
            bool Placed = false;
            double x = e.GetPosition(null).X;
            double y = e.GetPosition(null).Y;
            int row = -1;
            int col = -1;

            // Hit Test the gameboard to find the center square
            List<UIElement> hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(new Point(x, y), this) as List<UIElement>;

            if (hits.Count > 0)
            {

                foreach (UIElement hit in hits)
                {
                    if (hit is Rectangle)
                    {
                        Rectangle rect = (Rectangle)hit;
                        row = Grid.GetRow(rect);
                        col = Grid.GetColumn(rect);

                        // Adjust the play location (snap to board)
                        Point MouseOffset = e.GetPosition(rect);
                        double newTop = (double)piece.GetValue(Canvas.TopProperty) - MouseOffset.Y + (piece.TileSize / 2);
                        double newLeft = (double)piece.GetValue(Canvas.LeftProperty) - MouseOffset.X + (piece.TileSize / 2);
                        piece.SetValue(Canvas.TopProperty, newTop);
                        piece.SetValue(Canvas.LeftProperty, newLeft);
                        
                        // Set the square statuses
                        List<Offset> vectors = piece.CurrentVectors;
                        foreach (Offset v in vectors)
                        {
                            int nRow = row + v.Y;
                            int nCol = col + v.X;
                            this.squares[nRow][nCol].Status = (Square.SquareStatus)piece.PlayerNumber;
                        }
                        piece.Parent.IsFirstMove = false;
                        Placed = true;
                        break;
                    }
                }                
            }

            return Placed;
        }

        private bool PieceOnStarterSquare(Piece piece, int row, int col)
        {
            bool onStarter = false;

            List<Offset> vectors = piece.CurrentVectors;

            foreach (Offset v in vectors)
            {
                int nRow = row + v.Y;
                int nCol = col + v.X;

                if ((piece.PlayerNumber == 0) && (nRow == PURPLE_STARTER_ROW) && (nCol == PURPLE_STARTER_COL))
                {
                    onStarter = true;
                    break;
                }
                else if ((piece.PlayerNumber == 1) && (nRow == ORANGE_STARTER_ROW) && (nCol == ORANGE_STARTER_COL))
                {
                    onStarter = true;
                    break;
                }
            }

            return onStarter;
        }     

        private bool PieceFits(Piece piece, int row, int col)
        {
            bool fits = true;

            List<Offset> vectors = piece.CurrentVectors;

            foreach (Offset v in vectors)
            {
                if (this.GetSquareStatus(row + v.Y, col + v.X) != Square.SquareStatus.None)
                {
                    fits = false;
                    break;
                }
            }

            return fits;
        }

        private bool PieceTouchesCorner(Piece piece, int row, int col)
        {
            bool good = false;

            List<Offset> vectors = piece.CurrentVectors;

            // TODO: Refactor this?
            Square.SquareStatus player = (Square.SquareStatus)piece.PlayerNumber;

            foreach (Offset v in vectors)
            {
                int nRow = row + v.Y;
                int nCol = col + v.X;

                // Check upper-left
                if (this.GetSquareStatus(nRow - 1, nCol - 1) == player)
                {
                    good = true;
                    break;
                }
                // Check upper-right
                else if (this.GetSquareStatus(nRow - 1, nCol + 1) == player)
                {
                    good = true;
                    break;
                }
                // Check bottom-left
                else if (this.GetSquareStatus(nRow + 1, nCol - 1) == player)
                {
                    good = true;
                    break;
                }
                // Check bottom-right
                else if (this.GetSquareStatus(nRow + 1, nCol + 1) == player)
                {
                    good = true;
                    break;
                }
            }
            return good;
        }

        private bool PieceDoesNotConflict(Piece piece, int row, int col)
        {
            bool good = true;

            List<Offset> vectors = piece.CurrentVectors;

            // TODO: Refactor this?
            Square.SquareStatus player = (Square.SquareStatus)piece.PlayerNumber;

            foreach (Offset v in vectors)
            {
                int nRow = row + v.Y;
                int nCol = col + v.X;

                // Check left
                if (this.GetSquareStatus(nRow, nCol - 1) == player)
                {
                    good = false;
                    break;
                }
                // Check right
                else if (this.GetSquareStatus(nRow, nCol + 1) == player)
                {
                    good = false;
                    break;
                }
                // Check top
                else if (this.GetSquareStatus(nRow - 1, nCol) == player)
                {
                    good = false;
                    break;
                }
                // Check bottom
                else if (this.GetSquareStatus(nRow + 1, nCol) == player)
                {
                    good = false;
                    break;
                }
            }
            return good;
        }

        public void Reset()
        {            
            foreach (Square[] row in squares)
            {
                foreach (Square sq in row)
                {
                    sq.Status = Square.SquareStatus.None;
                }
            }

            // throw our GameReset event
            if (GameReset != null)
                GameReset(this);
                               
        }

	}

   
}